home *** CD-ROM | disk | FTP | other *** search
Text File | 1996-05-21 | 8.6 KB | 283 lines | [TEXT/ttxt] |
- --<<<
- -- Filename:
- -- grapher.sx
-
- -- Other Files Required:
- -- axisprop.sx, labels.sx, axis.sx, graphable.sx, grphmenu.sx
-
- -- Purpose:
- -- Create a Grapher class, a model for graphing objects in a two-dimensional graph.
-
- -- Specialized Classes:
- -- Grapher
-
- -- Instructions to User:
- -- The Grapher class will graph any object who has an instance variable that matches
- -- either the current x-axis property or the y-axis property. If the object does not
- -- have both, it will be graphed at the minimum value on the axis whose property it
- -- does not have.
- -- The property mapped on the axis can be changed via a popup menu of object
- -- properties. Axis labels and object positions will then be changed accordingly.
- -- This is a model for graphing, divorced from presentation.
- -- See vanilla.sx and autograph.sx for examples of how graphing can be wedded
- -- to two different types of layout.
-
- -- Author:
- -- Dionn Stewart, Steve Gano, Steve Mayer, Ray Davis
- in module Autofinder
-
- class Grapher (TwoDSpace)
- instance variables
- xAxis -- horizontal axis definition and associated labels
- yAxis -- vertical axis definition and associated labels
- objectList -- the list of objects to graph or graphed
- propertyList -- list of graphable properties
- travelMaster -- masterclock for all the travelers
- travelDuration -- duration of interpolation
- propertyMenu -- menu of the propertylist
- background
- control
- end
-
- method init self {class Grapher} #rest args #key \
- boundary:(undefined) \
- background:(undefined) \
- xAxis: \
- yAxis: \
- propertyMenu: \
- propertyList:(new Array) \
- travelMaster:(new Clock) \
- travelDuration:(12) ->
- (
- if boundary = undefined do
- boundary := background.bbox
- apply nextMethod self boundary:boundary args
-
- -- if new properties are defined on the axes, add them to the menu
- if (xAxis.property <> undefined) and \
- (not (isMember propertyList xAxis.property)) do
- append propertyList xAxis.property
- if (yAxis.property <> undefined) and \
- (not (isMember propertyList yAxis.property)) do
- append propertyList yAxis.property
-
- -- Attach the menu to the invokers
- xAxis.axisLabel.menu:= propertyMenu
- yAxis.axisLabel.menu:= propertyMenu
-
- self.background := background
- prepend self background
- self.xAxis:= xAxis; prepend self xAxis
- self.yAxis:= yAxis; prepend self yAxis
- self.propertyList := propertyList
- self.propertyMenu := propertyMenu
-
- -- set up the clock that will control the traveling of all the objects
- travelMaster.rate:= 0
- self.travelMaster:= travelMaster
- self.travelMaster.scale:= 1
- self.travelDuration:= travelDuration
- self
- )
-
- method afterInit self {class Grapher} #rest args #key \
- objectList:(new Array) ->
- (
- apply nextMethod self args
-
- -- Init variables
- self.objectList := objectList
-
- -- Create controllers
- local dc := new DragController space:self wholespace:true
-
- -- Add a RowColumnController to the graph so when a group of objects get added
- -- they form a nice row before they are graphed
- local rc:= new RowColumnController space:self numColumns:6 numRows:5 layoutOrder:@rowMajor
- rc.defaultHeight := @highest
- rc.defaultWidth := @widest
-
- self.control := #(dc, rc)
-
- -- Add the properties
- for i in self.propertyList do
- addPropertytoMenu self i
-
- -- Set up the axis labels
- if self.propertyList.size > 0 do
- (
- if self.xAxis.property = undefined do
- changeProperty self.xAxis self.propertyList[1]
- if (self.propertyList.size > 1) and (self.yAxis.property = undefined) do
- changeProperty self.yAxis self.propertyList[2]
- )
-
- -- If we were given a group of initial objects, then position them on
- -- the graph.
- -- if (size objectList) > 0 do
- -- addObjectsToGraph self objectList
-
- self
- )
-
- method leaveScene self {class Grapher} ->
- (
- for i in self.control do
- i.space := undefined
- emptyout self.control
-
- emptyout self
- makePurgeable self.background
- self.background := self.xAxis := self.yAxis := undefined
- )
-
- -- Position the labels on the graph in the default fashion.
- method placeLabels self {class Grapher} ->
- (
- local orgx := self.xAxis.origin
- local orgy := self.yAxis.origin
-
- -- generically places the minLabel at the origin, the maxLabel at the right
- -- end and the axisLabel in the center below the axis line
- self.xAxis.minLabel.x:= orgx
- self.xAxis.minLabel.y:= orgy + 5
- self.xAxis.maxLabel.x:= orgx + self.xAxis.length - self.xAxis.maxLabel.width
- self.xAxis.maxLabel.y:= orgy + 5
- self.xAxis.axisLabel.x:= orgx + ((self.xAxis.length/2) - (self.xAxis.axisLabel.width/2)) - 10
- self.xAxis.axisLabel.y:= orgy + 25
-
- -- generically places the minLabel at the origin, the maxLabel at the top
- -- end and the axisLabel in the center, all to the left of the axis line
- local labelx := orgx - 5
- self.yAxis.minLabel.x:= labelx - self.yAxis.minLabel.width
- self.yAxis.minLabel.y:= orgy - self.yAxis.minLabel.height
- self.yAxis.maxLabel.x:= labelx - self.yAxis.maxLabel.width
- self.yAxis.maxLabel.y:= orgy - self.yAxis.length
- self.yAxis.axisLabel.x:= labelx - self.yAxis.axisLabel.width - 16
- self.yAxis.axisLabel.y:= orgy - (self.yAxis.length/2) -
- (self.yAxis.axisLabel.height/2)
- undefined
- )
-
- method addPropertyToMenu self {class Grapher} prop ->
- (
- -- add the pushbutton to the menu
- addPropertyToMenu self.propertyMenu prop
-
- if not (isMember self.propertyList prop) do append self.propertyList prop
- )
-
- -- Add a group of objects to the grapher, starting them off in a tidy row
- method addObjectsToGraph self {class Grapher} objList ->
- (
- local rc := (chooseOne self.controllers \
- (rc dummy -> isAKindOf rc RowColumnController) undefined)
- local pos := 1
-
- foreach objList (obj arg ->
- pos := pos + 1
- addObjectToGraph self obj position:pos
- -- Add the object to the RowColumn controller
- append rc obj
- ) undefined
- undefined
- )
-
- -- subclasses or instances can override this (must call nextMethod though)
- -- if they want to do anything else when a graphable object is added
- -- to the grapher
- method addObjectToGraph self {class Grapher} obj #key position:->
- (
- append self.objectList obj
- if position = unsupplied then
- prepend self obj
- else
- addNth self position obj
- if obj.travelClock.masterclock = undefined do
- obj.travelClock.masterClock := self.travelMaster
- undefined
- )
-
- -- subclasses or instances can override this (must call nextMethod though)
- -- if they want to do anything else when a graphable object is removed
- -- from the grapher
- method removeFromGraph self {class Grapher} obj->
- (
- deleteone self obj
- deleteone self.objectList obj
- undefined
- )
-
- method graphObjects self {class Grapher} objList #key duration: ->
- (
- -- Remove all objects from the RowColumn controller for graphing.
- emptyout (chooseOne self.controllers \
- (rc dummy -> isAKindOf rc RowColumnController) undefined)
-
- self.travelMaster.rate:= 0
- for obj in objList do
- graphObject self obj duration:duration wait:true
-
- -- start the master clock which will start all the travelers travelling
- self.travelMaster.rate:= 1
- )
-
- method graphObject self {class Grapher} obj #key duration: wait:(false) ->
- (
- local newX, newY, Xprop, Yprop, ivGetter, g
- Xprop:= self.xAxis.property
- Yprop:= self.yAxis.property
- if Xprop <> undefined then
- (
- -- get the appropriate getter function for the instance variable that
- -- corresponds to the axis property
- ivGetter := Xprop.getterFn
- newX:= self.xAxis.origin + (scaleValue self.xAxis Xprop (ivGetter obj)) -obj.tackPoint.x
- ) else
- newX:= self.xAxis.origin - obj.tackPoint.x
-
- if Yprop <> undefined then
- (
- ivGetter := Yprop.getterFn
- newY:= self.yAxis.origin - (scaleValue self.yAxis Yprop (ivGetter obj)) -obj.tackPoint.y
- ) else
- newY:= self.yAxis.origin - obj.tackPoint.y
-
- if duration = unsupplied do duration := self.travelDuration
- travel obj (new Point x:newX y:newY) duration
- )
-
- -- For use of Tape Measure and other such parties
- method getScale self {class Grapher} ->
- (
- local xprop, yprop
- xprop:= self.xAxis.property
- yprop:= self.yAxis.property
- -- structure is an array where each item is a description of each dimension
- -- where the description consists of the units of measurements and the
- -- pixels per unit conversion factor
- #(#(xprop.units, self.xAxis.scale), #(yprop.units, self.yAxis.scale))
- )
-
- method refreshGraph self {class Grapher} ->
- (
- if (size self.objectList) > 0 do
- graphObjects self self.objectList
-
- -- Notify other interested parties (e.g., the Tape Measure).
- foreach self ( item arg ->
- (
- if (canObjectDo item refreshGraph) do refreshGraph item
- if (isDefined updateScale) and (canObjectDo item updateScale) do updateScale item
- ) ) undefined
- )
-
- --method startGraphing self {class Grapher} objList ->
- --(
- -- addObjectsToGraph self objList
- -- graphObjects self
- --)
-
- "Compiled grapher.sx"
- -->>>
-